package com.heima.search.service.impl;

import apis.article.IArticleClient;
import com.alibaba.fastjson.JSON;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.SearchContants;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.model.search.vos.SearchArticleVo;
import com.heima.search.service.ArticleSearchService;
import com.heima.search.service.SearchHistoryService;
import com.heima.utils.thread.ThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private SearchHistoryService searchHistoryService;
    @Autowired
    private IArticleClient articleClient;

    /**
     * ES文章分页搜索
     * @param userSearchDto
     * @return
     */
    public ResponseResult search(UserSearchDto userSearchDto) throws IOException {
        if (userSearchDto == null || StringUtils.isEmpty(userSearchDto.getSearchWords())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //构建查询语句
        SearchRequest searchRequest = new SearchRequest(SearchContants.SEARCH_ARTICLE_INDEX);
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(userSearchDto.getSearchWords(), "title", "content"));
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(userSearchDto.getMinBehotTime().getTime()));
        searchRequest.source().query(boolQueryBuilder);
        searchRequest.source().from(0);
        searchRequest.source().size(userSearchDto.getPageSize());
        searchRequest.source().sort("publishTime", SortOrder.DESC);
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        searchRequest.source().highlighter(highlightBuilder);

        //发送查询请求
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        List<Map> list = new ArrayList<>();
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            String source = hit.getSourceAsString();
            Map map = JSON.parseObject(source, Map.class);
            Map<String, HighlightField> titles = hit.getHighlightFields();
            if (titles != null && titles.size() > 0) {
                String h_title = StringUtils.join(titles.get("title").getFragments());
                map.put("h_title", h_title);
            } else {
                map.put("h_title", map.get("title"));
            }
            list.add(map);
        }
        if (ThreadLocalUtil.get() != null && userSearchDto.getPageNum() == 0) {
            //insert是一个Async修饰的异步方法，需要传入userId，无法通过threadlocal获取
            searchHistoryService.insert(ThreadLocalUtil.get(), userSearchDto.getSearchWords());
        }
        return ResponseResult.okResult(list);
    }

    /**
     * ES文章新增功能
     * @param message
     */
    @Override
    public void add(String message) {
        if (StringUtils.isEmpty(message)) {
            return;
        }
        SearchArticleVo article = JSON.parseObject(message, SearchArticleVo.class);
        IndexRequest index = new IndexRequest(SearchContants.SEARCH_ARTICLE_INDEX);
        index.id(article.getId().toString());
        index.source(message, XContentType.JSON);
        try {
            restHighLevelClient.index(index, RequestOptions.DEFAULT);
        } catch (IOException e) {
            //只记录错误日志，异步线程不建议抛出异常，因为不是前端直接操作
            e.printStackTrace();
            log.error("sync es error={}", e);
        }
    }

    /**
     * 文章上下架，ES文章同步功能
     * @param message
     */
    @Override
    public void upOrDown(String message) throws IOException {
        if (StringUtils.isEmpty(message)) {
            return;
        }
        Map map = JSON.parseObject(message, Map.class);
        Long articleId = (Long) map.get("articleId");
        Integer enable = (Integer) map.get("enable");
        if (enable == ArticleConstants.ARTICLE_TAKEDOWN) {
            DeleteRequest del = new DeleteRequest(SearchContants.SEARCH_ARTICLE_INDEX);
            del.id(articleId.toString());
            restHighLevelClient.delete(del, RequestOptions.DEFAULT);
        } else if (enable == ArticleConstants.ARTICLE_EDABLE) {
            ResponseResult<SearchArticleVo> responseResult = articleClient.selectESVoById(articleId);
            if (responseResult.getCode() == 200 && responseResult.getData() != null) {
                SearchArticleVo searchArticleVo = responseResult.getData();
                add(JSON.toJSONString(searchArticleVo));
            }
        }
    }
}